﻿using System;
using System.Windows.Input;
using Hopscotch.Common.Contracts.AppService;
using Hopscotch.Domain.Common;
using Hopscotch.Presentation.Common.Commands;
using Hopscotch.Presentation.Common.Constants;
using Hopscotch.Presentation.Common.Threading;
using Microsoft.Practices.Prism.Commands;

namespace Hopscotch.Presentation.Common
{
    public interface IToolViewModel
    {
        /// <summary>
        /// Track if the user has attemped to Save. This avoids showing error icons from the start.
        /// </summary>
        bool IsSavePressed { get; set; }

        /// <summary>
        /// Is the Interop control enabled
        /// </summary>
        bool IsInteropReactEnabled { get; set; }

        /// <summary>Is Busy</summary>
        bool IsBusy { get; set; }

        bool HasErrors { get; }

        ToolEditMode ToolEditMode { get; set; }
        
        ToolEditMode GetFieldEditMode(string key);

        void Validate();
    }


    public interface IToolViewModel<T, TKey> : IToolViewModel
        where T : DomainBase<TKey>
        where TKey : struct
    {
        T Model { get; }
        void SetModel(T entity);
        

        void RaiseErrorNotification(string errorMessage, Exception exception);
    }

    public abstract class ToolViewModel<T, TKey> : ViewModelBase, IToolViewModel<T, TKey>
        where T : DomainBase<TKey>, ICloneable<T>
        where TKey : struct
    {
        public T Model { get; private set; }
        public T RollbackModel { get; private set; }
        public bool IsSavePressed { get; set; }

        private readonly IDomainService<T, TKey> domainService;

        //Commands
        public ICommandBase NewEntityCommand { get; private set; }
        public ICommandBase SaveEntityCommand { get; private set; }
        public ICommandBase UndoEditCommand { get; private set; }
        public ICommand CancelEditCommand { get; private set; }
        public ICommandBase DeleteEntityCommand { get; private set; }
        public ICommandBase EditLayoutCommand { get; private set; }

        public ToolViewModel()
        {
        }

        public ToolViewModel(IDispatcher dispatcher,
            IDomainService<T, TKey> domainService)
        {
            this.domainService = domainService;

            //Wire-up commands
            this.NewEntityCommand = new NewEntityCommand<T, TKey>(this);
            this.SaveEntityCommand = new SaveEntityCommand<T, TKey>(dispatcher, this, domainService);
            this.CancelEditCommand = new DelegateCommand(Cancel);
            this.DeleteEntityCommand = new DeleteEntityCommand<T, TKey>(this, domainService);
            this.EditLayoutCommand = new EditLayoutCommand();
        }

        private bool isInteropReactEnabled;
        public virtual bool IsInteropReactEnabled
        {
            get { return isInteropReactEnabled; }
            set
            {
                if (isInteropReactEnabled == value)
                    return;

                isInteropReactEnabled = value;
                RaisePropertyChanged();
            }
        }

        private bool isBusy;
        public virtual bool IsBusy
        {
            get { return isBusy; }
            set
            {
                if (isBusy == value)
                    return;

                isBusy = value;
                RaisePropertyChanged();
            }
        }

        private bool isNotifyVisible;
        public virtual bool IsNotifyVisible
        {
            get { return isNotifyVisible; }
            set
            {
                if (isNotifyVisible == value)
                    return;

                isNotifyVisible = value;
                RaisePropertyChanged();
            }
        }

        private ToolEditMode toolEditMode;
        public ToolEditMode ToolEditMode
        {
            get { return toolEditMode; }
            set
            {
                if (toolEditMode == value)
                    return;

                toolEditMode = value;

                RaisePropertyChanged();
                OnEditModeChanged();
            }
        }
        
        private InteropReactState? previousInteropReactState;

        private InteropReactState selectedInteropReactState;
        public InteropReactState SelectedInteropReactState
        {
            get { return selectedInteropReactState; }
            set
            {
                if (selectedInteropReactState == value)
                    return;

                selectedInteropReactState = value;

                RaisePropertyChanged();
            }
        }

        public virtual void OnEditModeChanged()
        {
            if (ToolEditMode == Common.ToolEditMode.None)
            {
                IsInteropReactEnabled = true;

                if (previousInteropReactState.HasValue)
                    SelectedInteropReactState = previousInteropReactState.Value;

                previousInteropReactState = null;
            }

            if (ToolEditMode == Common.ToolEditMode.New ||
                ToolEditMode == Common.ToolEditMode.Update)
            {
                IsInteropReactEnabled = false;
                previousInteropReactState = previousInteropReactState ?? SelectedInteropReactState;
                SelectedInteropReactState = InteropReactState.Red;
            }

            NewEntityCommand.RaiseCanExecuteChanged();
        }

        private void Cancel()
        {
            T model = null;
            if(RollbackModel != null)
                model = RollbackModel.Clone(true);

            ToolEditMode = ToolEditMode.None;

            SetModel(model);           
        }

        public override void Validate()
        {
            base.Validate();

            if (Model != null)
            {
                ToolEditMode = ToolEditModeHelper.Map(Model.ChangeTracker);
            }
        }

        protected abstract T CreateNewEntity();

        public void SetModel(T model)
        {
            IsSavePressed = false;
            IsInteropReactEnabled = true;

            if (model == null)
                model = CreateNewEntity();
            else
            {
                RollbackModel = model.Clone(true);
            }

            this.Model = model;

            ClearErrors();

            RaisePropertyChanged(string.Empty);
        }

        public ToolEditMode GetFieldEditMode(string key)
        {
            ToolEditMode toolEditMode = ToolEditMode.None;

            if (this.Model != null && this.Model.ChangeTracker != null)
            {
                var changeTracker = this.Model.ChangeTracker;
                if (changeTracker != null)
                {
                    toolEditMode = ToolEditModeHelper.Map(changeTracker);
                    if (toolEditMode == ToolEditMode.New)
                    {
                        //For new, return VM property so it starts and None
                        return ToolEditMode;
                    }
                    if (toolEditMode == ToolEditMode.Update)
                    {
                        if (changeTracker.OriginalValues != null &&
                            changeTracker.OriginalValues.ContainsKey(key))
                        {
                            //Only show update if this field has been updated
                            return toolEditMode;
                        }
                        toolEditMode = ToolEditMode.None;
                    }                    
                }
            }

            return toolEditMode;
        }

        public abstract void RaiseErrorNotification(string errorMessage, Exception exception);
    }
}
